//
//	HOME AUTOMATION GATEWAY PROJECT
//
//  (c) 2008 mocom software GmbH & Co KG
//	for European Microsoft Innovation Center
//
//  $Author: volker $
//  $Date: 2009-10-17 15:38:02 +0200 (Sa, 17. Okt 2009) $
//  $Revision: 528 $
//
//  Microsoft dotNetMF Project
//  Copyright ©2001,2002,2003,2004 Microsoft Corporation
//  One Microsoft Way, Redmond, Washington 98052-6399 U.S.A.
//  All rights reserved.
//  MICROSOFT CONFIDENTIAL
//
//-----------------------------------------------------------------------------

#include <tinyhal.h>
#include "AT91_GPIO.h"
#include "HAG_I2C_GPIO.h"
#include "..\HAG_PAL\i2c\i2c_ex_decl.h"

//--//

#undef  TRACE_ALWAYS

#define TRACE_ALWAYS               0x00000001

#undef  DEBUG_TRACE
#define DEBUG_TRACE (TRACE_ALWAYS)

#if DEBUG
#define DBG				hal_printf
#define DBGGPIO(f,x)	hal_printf(f ": " #x "=%d\r\n",x)
#else
#define DBGGPIO(f,x)
#define DBG				nul
static void nul(char *fmt, ...)
{
}
#endif

//--//

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rodata = "c_Gpio_Attributes_AT91"
#endif

const UINT8 HAG_I2C_GPIO_Driver::c_Gpio_Attributes[HAG_I2C_GPIO_Driver::c_MaxPins] =
{
    GPIO_ATTRIBUTE_INPUT,	//	0   (register 0)
    GPIO_ATTRIBUTE_INPUT,	//	1
    GPIO_ATTRIBUTE_INPUT, 	//	2
    GPIO_ATTRIBUTE_INPUT, 	//  	3
	GPIO_ATTRIBUTE_OUTPUT,	// 	4
	GPIO_ATTRIBUTE_OUTPUT,	// 	5
	GPIO_ATTRIBUTE_OUTPUT,	// 	6
	GPIO_ATTRIBUTE_OUTPUT	// 	7
};

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section rodata
#endif

//--//

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata = "gHAG_I2C_GPIO_Driver_GPIO_Driver"
#endif

HAG_I2C_GPIO_Driver gHAG_I2C_GPIO_Driver;

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata
#endif

//--//
static I2C_USER_CONFIGURATION c_8575Config = {0x42>>1,100};

void HAG_I2C_GPIO_Driver::PC8575_Write_Outputs()
{
	gHAG_I2C_GPIO_Driver.m_InitDone = c_InitDone;

	gHAG_I2C_GPIO_Driver.m_i2cXactionUnitWriteRef[0] =
		&gHAG_I2C_GPIO_Driver.m_i2cXactionUnitWrite;

	I2C_InitializeTransactionUnit(&gHAG_I2C_GPIO_Driver.m_i2cXactionUnitWrite,
								  gHAG_I2C_GPIO_Driver.m_OutputData,
								  gHAG_I2C_GPIO_Driver.m_txbuf,
								  2,
								  false);

	I2C_InitializeTransaction(&gHAG_I2C_GPIO_Driver.m_i2cXactionWrite,
							  c_8575Config,
							  gHAG_I2C_GPIO_Driver.m_i2cXactionUnitWriteRef,
							  1);

	I2C_EnqueueEx( &gHAG_I2C_GPIO_Driver.m_i2cXactionWrite, I2CCallbackTx );
}

void HAG_I2C_GPIO_Driver::PC8575_Read_Inputs()
{

	gHAG_I2C_GPIO_Driver.m_i2cXactionUnitReadRef[0] =
		&gHAG_I2C_GPIO_Driver.m_i2cXactionUnitRead;

	I2C_InitializeTransactionUnit(&gHAG_I2C_GPIO_Driver.m_i2cXactionUnitRead,
								  gHAG_I2C_GPIO_Driver.m_rxbuf,
								  gHAG_I2C_GPIO_Driver.m_rxbuf,
								  2,
								  true);

	I2C_InitializeTransaction(&gHAG_I2C_GPIO_Driver.m_i2cXactionRead,
							  c_8575Config,
							  gHAG_I2C_GPIO_Driver.m_i2cXactionUnitReadRef,
							  1);

	I2C_EnqueueEx( &gHAG_I2C_GPIO_Driver.m_i2cXactionRead, I2CCallback );
}

BOOL HAG_I2C_GPIO_Driver::Initialize()
{
	// init variables
	gHAG_I2C_GPIO_Driver.m_OutputData[0] = 0xff;
	gHAG_I2C_GPIO_Driver.m_OutputData[1] = 0xff;
	gHAG_I2C_GPIO_Driver.m_IOMap=0;
	gHAG_I2C_GPIO_Driver.m_IOMap_Old=0;
	gHAG_I2C_GPIO_Driver.m_pending=0;
	gHAG_I2C_GPIO_Driver.m_txactions=0;
	gHAG_I2C_GPIO_Driver.m_InitDone=0;
	gHAG_I2C_GPIO_Driver.m_warmstart=false;

	for (int i=0;i<c_MaxPins;i++) {
		gHAG_I2C_GPIO_Driver.m_PinReservationInfo[i] = false;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[i].code = NULL;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[i].data = NULL;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[i].mode = 0;
	}

	// enable ISR
	CPU_GPIO_EnableInputPin2(I2C_GPIO_IRQ_PIN,
						 FALSE,
						 (GPIO_INTERRUPT_SERVICE_ROUTINE)HAG_I2C_GPIO_Driver::GPIOISRVector,
						 0,
						 GPIO_INT_EDGE_LOW,
						 RESISTOR_PULLUP);

    return TRUE;
}

void HAG_I2C_GPIO_Driver::I2C_IS_UP()
{
	// make sure we are called once only
	if (gHAG_I2C_GPIO_Driver.m_InitDone != c_InitDone) 
	{
		// enable ISR if we are in a warmstart!
		// this is a workaround to the fact that ::Initialize() is called at coldstart ONLY		
		if (gHAG_I2C_GPIO_Driver.m_warmstart) {
			// reenable isr
			CPU_GPIO_EnableInputPin2(I2C_GPIO_IRQ_PIN,
								 FALSE,
								 (GPIO_INTERRUPT_SERVICE_ROUTINE)HAG_I2C_GPIO_Driver::GPIOISRVector,
								 0,
								 GPIO_INT_EDGE_LOW,
								 RESISTOR_PULLUP);
			// we had our fun, make sure we are ready for a cold start, even 
			// for that we reload from NAND and this SHOULD not be needed.
			gHAG_I2C_GPIO_Driver.m_warmstart=false;
		}
		
		// write outputs. will set m_InitDone to c_InitDone 
		HAG_I2C_GPIO_Driver::PC8575_Write_Outputs();
	}
}

//--//

BOOL HAG_I2C_GPIO_Driver::Uninitialize()
{
	// kill all interrupt leases
	for (int i=0;i<c_MaxPins;i++) {
		gHAG_I2C_GPIO_Driver.m_PinReservationInfo[i] = false;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[i].code = NULL;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[i].data = NULL;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[i].mode = 0;
	}
	
	// reset output
	gHAG_I2C_GPIO_Driver.m_OutputData[0] = 0xff;
	gHAG_I2C_GPIO_Driver.m_OutputData[1] = 0xff;
	
	// mark warmstart and reset m_InitDone
	gHAG_I2C_GPIO_Driver.m_warmstart=true;
	gHAG_I2C_GPIO_Driver.m_InitDone = 0;

    return TRUE;
}

//--//

UINT32 HAG_I2C_GPIO_Driver::Attributes( GPIO_PIN Pin )
{
	DBGGPIO("Attributes",Pin);

	if(Pin < c_MaxPins)
    {
        return c_Gpio_Attributes[Pin];
    }
    return GPIO_ATTRIBUTE_NONE;
}

//--//

void HAG_I2C_GPIO_Driver::DisablePin( GPIO_PIN pin, GPIO_RESISTOR resistorState, UINT32 dir_out, GPIO_ALT_MODE function )
{
	gHAG_I2C_GPIO_Driver.m_IsrCallbacks[pin].code = NULL;
	gHAG_I2C_GPIO_Driver.m_IsrCallbacks[pin].data = NULL;
	gHAG_I2C_GPIO_Driver.m_IsrCallbacks[pin].mode = 0;
}

//--//

void HAG_I2C_GPIO_Driver::EnableOutputPin( GPIO_PIN pin, BOOL initialState )
{
	// there are no outputs to enable .. they are allways
	SetPinState(pin,initialState);
}

//--//

BOOL HAG_I2C_GPIO_Driver::EnableInputPin( GPIO_PIN pin, BOOL GlitchFilterEnable, GPIO_INTERRUPT_SERVICE_ROUTINE ISR, void* pinIsrParam, GPIO_INT_EDGE intEdge, GPIO_RESISTOR resistorState )
{
    if(ISR)
    {
		// attach isr
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[pin].code = ISR;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[pin].data = pinIsrParam;
		gHAG_I2C_GPIO_Driver.m_IsrCallbacks[pin].mode = intEdge;
    }

    return TRUE;
}


BOOL HAG_I2C_GPIO_Driver::GetPinState( GPIO_PIN pin )
{
    DBGGPIO("GetPinState",pin);

    ASSERT(pin < c_MaxPins);

	if (pin > 3) {
		return (~(gHAG_I2C_GPIO_Driver.m_OutputData[0] >> pin)) & 1;
	} else {
		return (gHAG_I2C_GPIO_Driver.m_IOMap >> pin) & 1;
	}
}

//--//

void HAG_I2C_GPIO_Driver::SetPinState( GPIO_PIN pin, BOOL pinState )
{
	if (pin > 3) {

		GLOBAL_LOCK(irq);

		// set io.
		if (pinState) {
			// LED ON
			gHAG_I2C_GPIO_Driver.m_OutputData[0] &= ~(1<<pin);
		} else {
			// LED OFF
			gHAG_I2C_GPIO_Driver.m_OutputData[0] |= (1<<pin);
		}
		gHAG_I2C_GPIO_Driver.m_OutputData[0] |= 0x0f;
		gHAG_I2C_GPIO_Driver.m_OutputData[1] = 0xff;

		gHAG_I2C_GPIO_Driver.m_txactions++;

		// if there is nothing pending
		if (gHAG_I2C_GPIO_Driver.m_txactions == 1)
			PC8575_Write_Outputs();
	}
}

void HAG_I2C_GPIO_Driver::I2CCallbackTx(void* Param)
{
	GLOBAL_LOCK(irq);

	// request complete
	if (gHAG_I2C_GPIO_Driver.m_txactions) {
		gHAG_I2C_GPIO_Driver.m_txactions--;
	}

	// request another write operation is there was a
	// call to SetPinState
	if (gHAG_I2C_GPIO_Driver.m_txactions) {
		PC8575_Write_Outputs();
	}
}

//--//

void HAG_I2C_GPIO_Driver::I2CCallback(void* Param)
{
	unsigned scan;
	int trigger;

	GLOBAL_LOCK(irq);

	// save I/O
	gHAG_I2C_GPIO_Driver.m_IOMap_Old = gHAG_I2C_GPIO_Driver.m_IOMap;
	gHAG_I2C_GPIO_Driver.m_IOMap = ((UINT32) ~gHAG_I2C_GPIO_Driver.m_rxbuf[0]) & 0xf;

	// request complete
	if (gHAG_I2C_GPIO_Driver.m_pending) {
		gHAG_I2C_GPIO_Driver.m_pending--;
	}

	// request another read operation is there was an
	// interrupt in between
	if (gHAG_I2C_GPIO_Driver.m_pending) {
		PC8575_Read_Inputs();
	}

	// any isr to call
	for (scan=0;scan<4;scan++) {
		if (gHAG_I2C_GPIO_Driver.m_IsrCallbacks[scan].code) {
			// check edge mode
			switch (gHAG_I2C_GPIO_Driver.m_IsrCallbacks[scan].mode) {
				case GPIO_INT_EDGE_LOW:
					trigger = gHAG_I2C_GPIO_Driver.m_IOMap_Old & ~gHAG_I2C_GPIO_Driver.m_IOMap;
					break;
				case GPIO_INT_EDGE_HIGH:
					trigger = ~gHAG_I2C_GPIO_Driver.m_IOMap_Old & gHAG_I2C_GPIO_Driver.m_IOMap;
					break;
				case GPIO_INT_EDGE_BOTH:
					trigger = gHAG_I2C_GPIO_Driver.m_IOMap_Old ^ gHAG_I2C_GPIO_Driver.m_IOMap;
					break;
				case GPIO_INT_LEVEL_HIGH:
					trigger = gHAG_I2C_GPIO_Driver.m_IOMap;
					break;
				case GPIO_INT_LEVEL_LOW:
					trigger = ~gHAG_I2C_GPIO_Driver.m_IOMap;
					break;
				default:
					trigger = 0;
			}
			trigger &= (1<<scan);
			if (trigger) {
				gHAG_I2C_GPIO_Driver.m_IsrCallbacks[scan].code(scan+c_FirstIo,(gHAG_I2C_GPIO_Driver.m_IOMap >> scan) & 1,gHAG_I2C_GPIO_Driver.m_IsrCallbacks[scan].data);
			}
		}
	}
}

//--//

void HAG_I2C_GPIO_Driver::GPIOISRVector( GPIO_PIN Pin, BOOL PinState, void* Param )
{
	// request a I/O read
	gHAG_I2C_GPIO_Driver.m_pending++;

	if (gHAG_I2C_GPIO_Driver.m_InitDone != c_InitDone) {
		PC8575_Write_Outputs();
	}

	// if there is nothing pending
	if (gHAG_I2C_GPIO_Driver.m_pending == 1)
		PC8575_Read_Inputs();
}


//--//

void HAG_I2C_GPIO_Driver::SetResistor( GPIO_PIN pin, GPIO_RESISTOR resistorState )
{
}

//--//

BOOL HAG_I2C_GPIO_Driver::PinIsBusy( GPIO_PIN pin )
{
	DBGGPIO("PinIsBusy",pin);

    return gHAG_I2C_GPIO_Driver.m_PinReservationInfo[pin];
}

//--//

BOOL HAG_I2C_GPIO_Driver::ReservePin( GPIO_PIN pin, BOOL fReserve )
{
    GLOBAL_LOCK(irq);

	DBGGPIO("ReservePin",pin);

	if (gHAG_I2C_GPIO_Driver.m_PinReservationInfo[pin] && fReserve)
		return FALSE;
	if (!gHAG_I2C_GPIO_Driver.m_PinReservationInfo[pin] && !fReserve)
		return FALSE;

	gHAG_I2C_GPIO_Driver.m_PinReservationInfo[pin] = fReserve;

    return TRUE;
}

//--//

void HAG_I2C_GPIO_Driver::GetPinsMap( UINT8* pins, size_t size )
{
    const UINT8*    src = c_Gpio_Attributes;
    UINT8* dst = pins;
    UINT32 maxpin = c_MaxPins;

    if ( size ==0 ) return;
    while ((size--) && (maxpin --))
    {
        *dst = *src;
        ++dst; ++src;
    }
}


